#include "ir.hpp"
#include <memory>
#include <sstream>

std::unique_ptr<std::string> to_koopa_text(std::unique_ptr<BaseAST>& ast)
{
    // 假设: 只包含一个函数, 函数一定返回int

    std::ostringstream decl, inst;
    auto               comp_unit_ptr = dynamic_cast<CompUnitAST*>(ast.get());

    std::cout << *comp_unit_ptr << "\n";
    visit_ast(std::move(comp_unit_ptr), decl, inst);

    return std::make_unique<std::string>(decl.str() + inst.str());
}

void visit_ast(CompUnitAST*&&      comp_unit,
               std::ostringstream& decl,
               std::ostringstream& inst)
{
    for (auto& func_def : comp_unit->func_defs) {
        // auto func_def_rptr = dynamic_cast<FuncDefAST*>(func_def.get());
        // visit_ast(func_def_rptr, decl, inst);
        visit_ast(dynamic_cast<FuncDefAST*>(func_def.get()), decl, inst);
    }
}

void visit_ast(FuncDefAST*&&       func_def,
               std::ostringstream& decl,
               std::ostringstream& inst)
{
    inst << "fun @" << *func_def->ident
         << "(): " << type_mapper.at(*func_def->func_type) << " {\n";
    for (auto& block : func_def->blocks) {
        visit_ast(dynamic_cast<BlockAST*>(block.get()), decl, inst);
    }
    inst << "}\n";
}

void visit_ast(BlockAST*&&         block,
               std::ostringstream& decl,
               std::ostringstream& inst)
{
    inst << "%entry:\n";
    for (auto& statement : block->statements) {
        visit_ast(dynamic_cast<StmtAST*>(statement.get()), decl, inst);
    }
}

void visit_ast(StmtAST*&&          statement,
               std::ostringstream& decl,
               std::ostringstream& inst)
{
    size_t i = 0;
    for (i = 0; i != statement->exps.size(); i++) {
        // inst << "\t%" << i << " = ";
        visit_ast(dynamic_cast<ExpAST*>(statement->exps[i].get()), decl, inst);
    }
    // 存疑, 一个statement应该有多少个expression?
    auto& [value_from, index] =
        std::get<3>(EXPRESSION_LIST[statement->exps[i - 1]->idx]).value();
    // assert(value_from == ValueFrom::expression);
    switch (value_from) {
        case ValueFrom::expression: inst << "\tret %" << index << "\n"; break;
        case ValueFrom::symbol:
            inst << "\tret " << std::get<int32_t>(SYMBOL_LIST[index].second)
                 << '\n';
            break;
    }
}

void visit_ast(ExpAST*&&           exp,
               std::ostringstream& decl,
               std::ostringstream& inst)
{
    visit_ast(dynamic_cast<UnaryExpAST*>(exp->unary_exp.get()), decl, inst);
}

void visit_ast(UnaryExpAST*&&      uexp,
               std::ostringstream& decl,
               std::ostringstream& inst)
{
    switch (uexp->stores) {
        case UnaryExpAST::UnaryExpStorage::pexp:
            visit_ast(dynamic_cast<PrimaryExpAST*>(uexp->pexp.get()), decl,
                      inst);
            break;
        case UnaryExpAST::UnaryExpStorage::op_uexp: {
            visit_ast(
                dynamic_cast<UnaryExpAST*>(std::get<1>(uexp->op_self).get()),
                decl, inst);

            auto& [op, src1, src2, res] = EXPRESSION_LIST[uexp->idx];

            switch (op) {
                case OperatorType::lnot: {
                    inst << "\t%" << uexp->idx << " = eq 0, ";
                    auto& [value_from, index] = src1.value();
                    switch (value_from) {
                        case ValueFrom::symbol: {
                            auto& [type, elem] = SYMBOL_LIST[index];
                            switch (type) {
                                case ValueType::i32:
                                    inst << std::get<int32_t>(elem) << "\n";
                                    break;
                                default: assert(false); break;
                            }
                            break;
                        }
                        case ValueFrom::expression:
                            inst << "%" << index << "\n";
                            break;
                        default: assert(false); break;
                    }
                    res = {ValueFrom::expression, uexp->idx};
                } break;
                case OperatorType::uadd: {
                    // uadd不生成任何代码
                    // 直接将结果设置为操作数
                    res = src1.value();
                }

                break;
                case OperatorType::uminus: {
                    inst << "\t%" << uexp->idx << " = sub 0, ";
                    auto& [value_from, index] = src1.value();
                    switch (value_from) {
                        case ValueFrom::symbol: {
                            auto& [type, elem] = SYMBOL_LIST[index];
                            switch (type) {
                                case ValueType::i32:
                                    inst << std::get<int32_t>(elem) << "\n";
                                    break;
                                default: assert(false); break;
                            }
                            break;
                        }
                        case ValueFrom::expression:
                            // 一般而言, index和result索引相同
                            // 但是对于一元加法 uadd
                            // 这种没有实际操作的表达式而言, 其值是上一层表达式
                            // inst << "%" << index << "\n";
                            inst << "%"
                                 << std::get<1>(
                                        std::get<3>(EXPRESSION_LIST[index])
                                            .value())
                                 << "\n";
                            break;
                        default: assert(false); break;
                    }
                    res = {ValueFrom::expression, uexp->idx};
                } break;
                default: assert(false); break;
            }
        } break;
        default: assert(false); break;
    }
}
void visit_ast(PrimaryExpAST*&&    pexp,
               std::ostringstream& decl,
               std::ostringstream& inst)
{
    switch (pexp->stores) {
        case PrimaryExpAST::PrimaryExpStorage::val:
            // visit_ast(dynamic_cast<ExpAST*>(pexp->exp.get()), decl, inst);
            break;
        case PrimaryExpAST::PrimaryExpStorage::exp:
            visit_ast(dynamic_cast<ExpAST*>(pexp->exp.get()), decl, inst);
            break;
        default: assert(false); break;
    }
}